package com.ihm.projetihm_v1.activity.photo;

import java.io.File;
import java.net.URL;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

import android.app.Activity;
import android.app.Fragment;
import android.app.FragmentTransaction;
import android.app.ProgressDialog;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.pm.ActivityInfo;
import android.graphics.Bitmap;
import android.graphics.drawable.BitmapDrawable;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.Handler;
import android.os.Handler.Callback;
import android.os.Message;
import android.preference.PreferenceManager;
import android.support.v4.app.NavUtils;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.ImageButton;
import android.widget.RelativeLayout;
import android.widget.Toast;

import com.ihm.projetihm_v1.MyApplication;
import com.ihm.projetihm_v1.R;
import com.ihm.projetihm_v1.activity.main.ImageDownloader;
import com.ihm.projetihm_v1.activity.main.listener.MainImageListener;
import com.ihm.projetihm_v1.activity.photo.listener.ArrowButton;
import com.ihm.projetihm_v1.activity.photo.listener.ImageListener;
import com.ihm.projetihm_v1.activity.preferences.PreferencesActivity;
import com.ihm.projetihm_v1.activity.preferences.PreferencesFragment;
import com.ihm.projetihm_v1.image.fragmentManager.ImageFragment;
import com.ihm.projetihm_v1.image.fragmentManager.ImageFragment.MY_TABS;
import com.ihm.projetihm_v1.nf.Graphe;
import com.ihm.projetihm_v1.nf.LinkedPhoto;
import com.ihm.projetihm_v1.nf.LinkedPhoto.RELATION_TYPE;
import com.ihm.projetihm_v1.nf.communication.CommunicationHTTP;

public class PhotoActivity 
	extends Activity
{
	
	private int mMaxWidth;
	
	private int mMaxHeight;
	
	private RelativeLayout myImageView;

	private Map<RELATION_TYPE, ArrowButton> mMapButtons;

	private Fragment mFragment;
	
	ProgressDialog mProgressDialog;
	
	public PhotoActivity()
	{
		this.mMaxHeight = this.mMaxWidth = -1;
	}
	
	@Override
	protected void onCreate(Bundle savedInstanceState)
	{
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_photo);
		
		// Show the Up button in the action bar.
		getActionBar().setDisplayHomeAsUpEnabled(true);
        
        //Get the IMAGE passed from MainActivity
        this.myImageView = (RelativeLayout) this.findViewById(R.id.id_photo_imageView);
        
        if (CURRENT_LINKED_PHOTO == null)
        {
        	// we have to determine the current linked photo from Intent
            Intent intename = getIntent();
            String filePath = (String) intename.getSerializableExtra(
            		MainImageListener.C_FILE_PATH
            	);
            LinkedPhoto tmpFile = this.findLinkedPhoto(filePath);
            if (tmpFile == null) // panorama
            {
            	tmpFile = new LinkedPhoto(new File(filePath));
            }
            CURRENT_LINKED_PHOTO = tmpFile;
            
            //if connection with server
			if (this.isConnectedToServer())
			{
	            this.attachGraphFragment();
			}
        }
        
        //add a listener on the image
        this.myImageView.setOnTouchListener(new ImageListener(this));
        
        //Stock the arrows of the activity
        this.initMapButton(); 
        
        // set new file
        this.setFile(CURRENT_LINKED_PHOTO);
	 
        // compute image dimension from screen dimension
//      handler.removeMessages(0);
//      handler.sendEmptyMessageDelayed(0, 40);
    }
	@Override
	public boolean onCreateOptionsMenu(Menu menu) {
		// Inflate the menu; this adds items to the action bar if it is present.
		getMenuInflater().inflate(R.menu.activity_photo, menu);
		if (this.isConnectedToServer())
		{
			MenuItem item = menu.findItem(R.id.menu_connect_to_server);
			item.setTitle(this.getString(R.string.menu_disconnect_to_server));
		}
		return true;
	}

	@Override
	public boolean onOptionsItemSelected(MenuItem item)
	{
		switch (item.getItemId())
		{
		case android.R.id.home:
			NavUtils.navigateUpFromSameTask(this);
			return true;
		case R.id.menu_settings:
			Intent intent = new Intent(this, PreferencesActivity.class);
			startActivity (intent);
			return true;
		case R.id.menu_connect_to_server:
			String titleDisconnect = this.getString(R.string.menu_disconnect_to_server);
			String titleConnect = this.getString(R.string.menu_connect_to_server);
			if (item.getTitle().equals(titleDisconnect))
			{
				item.setTitle(titleConnect);
				return this.disconnectToServer();
			}
			else
			{
				item.setTitle(titleDisconnect);
				return this.connectToServer();
			}
		}
		return super.onOptionsItemSelected(item);
	}
	
	private boolean disconnectToServer()
	{
		this.detachgraphFragment();
		((MyApplication) this.getApplication())
				.setCommunicationHTTP(null);
		Toast.makeText(
				this,
				"Disconnected",
				Toast.LENGTH_LONG
			).show();
		return true;
	}
	
	/**
	 * connect to server
	 * @return true if connection successes
	 */
	private boolean connectToServer()
	{
		SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);
		
		PreferencesFragment.PREFERENCE_ENUM pref = PreferencesFragment.PREFERENCE_ENUM.CONNEXION_IP;
		String pathStr = prefs.getString(pref.getKey(), pref.getDefaultValue());
		pref = PreferencesFragment.PREFERENCE_ENUM.CONNEXION_PORT;
		String portStr = prefs.getString(pref.getKey(), pref.getDefaultValue());

		try
		{
			int port = Integer.valueOf(portStr);
			URL myURL = new URL("http", pathStr, port, "");
			System.out.println("URL: " +myURL);
			((MyApplication) this.getApplication())
					.setCommunicationHTTP(new CommunicationHTTP(myURL));
			
			CommunicationAsyncTask com = new CommunicationAsyncTask("/", true);
			com.execute((Void) null);
			return true;
		}
		catch (Exception e)
		{
			Toast.makeText(this, "Exception: " + e, Toast.LENGTH_LONG).show();
			return false;
		}
	}
	
	/*
	 * Stock (the arrow button and visibility) in memory in a map
	 */
	private void initMapButton()
	{
		this.mMapButtons = new  HashMap<RELATION_TYPE, ArrowButton>();

		this.mMapButtons.put(RELATION_TYPE.NORTH,
							new ArrowButton(this, (ImageButton) this.myImageView.findViewById(R.id.button_north)
											, View.INVISIBLE, RELATION_TYPE.NORTH) );
		
		this.mMapButtons.put(RELATION_TYPE.NORTH_EAST,
							new ArrowButton(this, (ImageButton) this.myImageView.findViewById(R.id.button_northEast)
											, View.INVISIBLE, RELATION_TYPE.NORTH_EAST) );
		
		this.mMapButtons.put(RELATION_TYPE.EAST,
							new ArrowButton(this, (ImageButton) this.myImageView.findViewById(R.id.button_east)
											, View.INVISIBLE, RELATION_TYPE.EAST) );
		
		this.mMapButtons.put(RELATION_TYPE.SOUTH_EAST,
				new ArrowButton(this, (ImageButton) this.myImageView.findViewById(R.id.button_southEast)
											, View.INVISIBLE, RELATION_TYPE.SOUTH_EAST) );
		
		this.mMapButtons.put(RELATION_TYPE.SOUTH,
				new ArrowButton(this, (ImageButton) this.myImageView.findViewById(R.id.button_south)
											, View.INVISIBLE, RELATION_TYPE.SOUTH) );
		
		this.mMapButtons.put(RELATION_TYPE.SOUTH_WEST,
				new ArrowButton(this, (ImageButton) this.myImageView.findViewById(R.id.button_southWest) 
											, View.INVISIBLE, RELATION_TYPE.SOUTH_WEST) );
		
		this.mMapButtons.put(RELATION_TYPE.WEST,
				new ArrowButton(this, (ImageButton) this.myImageView.findViewById(R.id.button_west) 
											, View.INVISIBLE, RELATION_TYPE.WEST) );
		
		this.mMapButtons.put(RELATION_TYPE.NORTH_WEST,
				new ArrowButton(this, (ImageButton) this.myImageView.findViewById(R.id.button_northWest) 
											, View.INVISIBLE, RELATION_TYPE.NORTH_WEST) );
	}
	
	/**
	 * 
	 * @param filePath
	 * @return linked photo from path, null linkedphoto does not exist
	 */
	public LinkedPhoto findLinkedPhoto(String filePath){
		// Get the folder name of the image
		String str[]= filePath.split("/");
		String folderName = str [str.length - 2];
		
		//Get the graphe corresponding to the folder name
		List<Graphe<LinkedPhoto>> graphList = this.getGrapheList(filePath);
		Graphe<LinkedPhoto> graphe = graphList.get(0);
		for (Graphe<LinkedPhoto> actual : graphList)
		{
			if (actual.getFolderTitle().equals(folderName)){
				graphe = actual;
			}
		}
		
		List<LinkedPhoto> linkedPhotoList = (List<LinkedPhoto>) graphe.getItems();
		//Get the linkedPhoto related to the image
		for (LinkedPhoto actual :  linkedPhotoList)
		{
			if(actual.getFile().getAbsolutePath().equals(filePath)) {
				return actual;				
			}					
		}
		return null;
	}
	
	private List<Graphe<LinkedPhoto>> getGrapheList(String filePath){		
		//Get the graphe corresponding to the folder name
		MyApplication app = (MyApplication) this.getApplication();
		List<Graphe<LinkedPhoto>> graphList = app.getMainGraphsList();
		
		return graphList;	
	}
	
	/**
	 * each arrow button linked to another photo will set to visible.
	 * Set to invisible otherwise
	 */
	private void computeExistingArrow()
	{
		Map<RELATION_TYPE, LinkedPhoto> existingRelations
				= CURRENT_LINKED_PHOTO.getRelations();
		for (RELATION_TYPE relationType : RELATION_TYPE.values() )
	    {
			ArrowButton buttonActual = this.mMapButtons.get(relationType);
			if (existingRelations.containsKey(relationType))
			{
				buttonActual.setVisibility(View.VISIBLE);
			}
			else
			{
				buttonActual.setVisibility(View.INVISIBLE);
			}	
	    }
	}
	
	/**
	 * for each button
	 * if a linked photo exists, so the button is showed. Hidden otherwise
	 */
	private void actualizeVisibility()
	{
		for (RELATION_TYPE actual : RELATION_TYPE.values() )
	    {
			  ArrowButton buttonActual = this.mMapButtons.get(actual);
			  buttonActual.getImageButton()
			  		.setVisibility(buttonActual.getVisibility());
	    }		  		
	}
	
	/**
	 * Method that set the arrows visibility to VISIBLE if exist an image in this direction
	 */
	private void showExistingArrows()
	{
		this.computeExistingArrow();
		this.actualizeVisibility();
		ARE_ARROWS_VISIBLE = true;
	}

	/**
	 * set all arrows to invisible and actualize visibility in view
	 */
	private void hideAllArrows()
	{
		for (RELATION_TYPE actual : RELATION_TYPE.values() )
	    {
			ArrowButton buttonActual = this.mMapButtons.get(actual);
			buttonActual.setVisibility(View.INVISIBLE);	
	    }
		this.actualizeVisibility();
		ARE_ARROWS_VISIBLE = false;
	}
	
	/**
	 * Change the showed image for the passed in parameter
	 * @param filePath
	 */
	public void setFile(LinkedPhoto newFile)
	{
		CURRENT_LINKED_PHOTO = newFile;
		if ( ARE_ARROWS_VISIBLE)
		{
			this.showExistingArrows();
		}
		else
		{
			this.hideAllArrows();
		}
		this.reshape();
	}
	
	private boolean isConnectedToServer()
	{
		return ((MyApplication) this.getApplication()).getCommunicationHTTP()
				!= null;
	}

	public void goTo(LinkedPhoto.RELATION_TYPE relation_type)
	{
		this.goTo(CURRENT_LINKED_PHOTO.getlinkedPhotoFromRelationType(relation_type));
	}
	public void goTo(String newPhotoPath)
	{
		this.goTo(this.findLinkedPhoto(newPhotoPath));
	}
	private void goTo(LinkedPhoto newPhoto)
	{
		if (newPhoto != null)
		{
			if (isConnectedToServer())
			{
				// send a message to server
				String relativePath = "showPhoto?name="
							+ newPhoto.getFile().getAbsolutePath();
				CommunicationAsyncTask com = new CommunicationAsyncTask(relativePath, false);
				com.execute((Void) null);
			}
			this.setFile(newPhoto);
		}
	}
	
	/**
	 * to inverse arrow buttons visibility
	 * If arrows for existing links are visible, they will be invisible.
	 * And if they are invisible, the will be visible after this method
	 */
	public void inverseArrowButtonVisibility()
	{
		if (ARE_ARROWS_VISIBLE)
		{
			this.hideAllArrows();
		}
		else
		{
			this.showExistingArrows();
		}
	}
	
    /**
     * Handler 
     */
    private Handler handler = new Handler(new classHandler());
    
    private class classHandler
    		implements Callback
    {
    	public boolean handleMessage(Message msg) {
	      //execute the image reshape
	      reshape();
	      return true;
    	}
    }
    
    private void reshape(){
    	//calculate the max size
    	if (this.mMaxHeight <= 0 || this.mMaxWidth <= 0)
    	{
    		RelativeLayout ll = (RelativeLayout)this.findViewById(
    				R.id.id_photo_totalLayout);
    		this.mMaxHeight = ll.getHeight();
    		this.mMaxWidth = ll.getWidth();
            if (this.mMaxHeight == 0 || this.mMaxWidth == 0)
    		{
    	        handler.removeMessages(0);
    	        handler.sendEmptyMessageDelayed(0, 20);
    	        return;
    		}
    	}
        Bitmap bitmap = ImageDownloader.getScaledBitmapFromMaxSize(
        		CURRENT_LINKED_PHOTO.getFile().getAbsolutePath(),
         		this.mMaxWidth,
         		this.mMaxHeight);
        if (bitmap == null)
        {
        	System.out.println("reshape bitmap null");
        	return;
        }
        BitmapDrawable drawable = new BitmapDrawable(this.getResources(), bitmap);

        // size
        RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(
        		bitmap.getWidth(),
        		bitmap.getHeight()
        	);
        // position
        params.leftMargin = (this.mMaxWidth - bitmap.getWidth()) /2;
        params.topMargin = (this.mMaxHeight - bitmap.getHeight()) /2;
        
        this.myImageView.setLayoutParams(params);
        
        // set new photo visible
        this.myImageView.setBackgroundDrawable(drawable);
    }
    
    private void detachgraphFragment()
    {
    	if (this.mFragment != null)
    	{
            FragmentTransaction ft = getFragmentManager().beginTransaction();
        	ft.detach(this.mFragment);
        	ft.commit();
    	}
    }
    
    private void attachGraphFragment()
    {
    	MyApplication app = (MyApplication) this.getApplication();
    	
    	//Get the complete graphe
    	String filepath = CURRENT_LINKED_PHOTO.getFile().getAbsolutePath();
    	List<Graphe<LinkedPhoto>> graphList = this.getGrapheList(CURRENT_LINKED_PHOTO.getFile().getAbsolutePath());
		Graphe<LinkedPhoto> graphe = graphList.get(0);

		// Get the folder name of the image
		String str[]= filepath.split("/");
		String folderName = str [str.length - 2];
		
		//Find the graph of the photo showed by the activity
		for (Graphe<LinkedPhoto> actual : graphList)
		{
			if (actual.getFolderTitle().equals(folderName)){
				graphe = actual;
			}
		}
		
		//Set this graph to the application
		graphList = new LinkedList<Graphe<LinkedPhoto>>();
		System.out.println(graphe.toString());
		graphList.add(graphe);
    	app.setPhotoGraphsList(graphList);    	        	

        FragmentTransaction ft = getFragmentManager().beginTransaction();

    	// Check if the fragment is already initialized
		if (this.mFragment == null)
		{
			this.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
			//Specify that it's not the mainActivity
			app.setIsMainActivity(false);
			
			// If not, instantiate and add it to the activity
			this.mFragment = Fragment.instantiate(this, ImageFragment.class.getName());
			this.mFragment.setRetainInstance(true);
			
			// with good argument
			int tabPosition = MY_TABS.PHOTOS.ordinal();
            Bundle args = new Bundle();
            args.putInt(
            		ImageFragment.CST_ARG_SECTION_NUMBER,
            		tabPosition);
            this.mFragment.setArguments(args);
            
			ft.add(
					R.id.id_photo_listView,
					this.mFragment,
					"tabTag" + tabPosition);
			ft.commit();
		} else {
			// If it exists, simply attach it in order to show it
			ft.attach(this.mFragment).commit();
		}		
  	}

    
	//++++++++++++++++//
	// STATIC METHODS //
	//++++++++++++++++//

	/**
	 * the linked photo must be saved on orientation changing
	 */
	private static LinkedPhoto CURRENT_LINKED_PHOTO;

	private static boolean ARE_ARROWS_VISIBLE;
	
	public static void reinitializePhotoActivity()
	{
		CURRENT_LINKED_PHOTO = null;
		ARE_ARROWS_VISIBLE = false;
	}
	

	//+++++++++++++++//
		// INNER CLASSES //
		//+++++++++++++++//
		
		private class CommunicationAsyncTask extends AsyncTask<Void, Void, Boolean>
		{
			private String mRelativePath;
			private String mResult;
			private Exception mException;
			private boolean mIsForConnection;
			public CommunicationAsyncTask(String relativePath, boolean isForConnection)
			{
				this.mRelativePath = relativePath;
				this.mIsForConnection = isForConnection;
			}
			
			@Override
			protected void onPreExecute()
			{
				if (this.mIsForConnection)
				{
					PhotoActivity.this.mProgressDialog = ProgressDialog.show(
							PhotoActivity.this,
							"",
							"Connection...",
							true
						);
				}
			}

			@Override
			protected Boolean doInBackground(Void... params)
			{
				CommunicationHTTP comHTTP = ((MyApplication) PhotoActivity.this.getApplication()).getCommunicationHTTP();
				try
				{
					this.mResult = comHTTP.get(this.mRelativePath);
					return true;
				} catch (Exception e)
				{
					this.mException = e;
					return false;
				}
			}
			@Override
			protected void onPostExecute(final Boolean result)
			{
				if (this.mIsForConnection)
				{
					PhotoActivity.this.mProgressDialog.dismiss();
					if (result)
					{
						if (PhotoActivity.this.isConnectedToServer())
						{
							PhotoActivity.this.attachGraphFragment();
						}
						Toast.makeText(PhotoActivity.this, "Connected", Toast.LENGTH_LONG).show();
						System.out.println("OK: " + this.mResult);
					}
					else
					{
						Toast.makeText(PhotoActivity.this, "Connection failed", Toast.LENGTH_LONG).show();
						System.out.println("Exception: " + this.mException);
					}
				}
				else
				{
					if ( ! result)
					{
						Toast.makeText(PhotoActivity.this, "Problem Server", Toast.LENGTH_LONG).show();
						System.out.println("Exception: " + this.mException);
					}
				}
			}
		}
}
